import itertools

from appium import webdriver
import time
import pymysql
import json
import asyncio
import smtplib
from email.mime.text import MIMEText
from email.header import Header


class qxb_spider(object):
    """ 初始化apinum参数及数据库连接"""
    def __init__(self):
        # 加载配置文件
        cfg = json.loads(open('./config.json', 'r').read())
        self.cfg = cfg

        # 1. 进入app，并定位到主页
        self.driver = webdriver.Remote(cfg['webdriver_host'], cfg['desired_caps'])

        # 2. 读取分辨率备用
        self.height = int(self.driver.get_window_size()['height'])
        self.width = int(self.driver.get_window_size()['width']) * 0.5
        print('设备连接成功')
        time.sleep(10)  # 等待开屏广告

        # 3. 连接db并创建游标
        mysql_info = cfg['mysql_info']
        self.conn = pymysql.connect(host=mysql_info['host'], port=mysql_info['port'], user=mysql_info['user'],
                                    passwd=mysql_info['passwd'], db=mysql_info['db'])
        self.cursor = self.conn.cursor()
        print('数据库连接成功')

        # 4. 创建异步任务执行
        self.loop = asyncio.get_event_loop()

    """ 回退方法 """
    def back_to_search(self):
        back_btn_id = "iv_back"
        self.driver.find_element_by_id(back_btn_id).click()

    """ 首次进入 """
    def first_in(self):
        driver = self.driver
        driver.find_element_by_xpath("//*[@text='请输入企业名、股东高管、地址等']").click()

    """ 获取本次任务 """
    def ent_list_task(self):
        self.cursor.execute(self.cfg['check_sql'])
        return self.cursor.fetchall()

    """ 1. 企业概览信息 """
    def get_info(self, social_credit_code, ent_name):
        ent_info_list = [ent_name, social_credit_code]
        ent_input_id = "et_search_content"

        input_btn = self.driver.find_element_by_id(ent_input_id)
        input_btn.clear()
        input_btn.send_keys(social_credit_code)
        time.sleep(1)
        # 1. 默认选取第一家企业,点击进入
        ent_first_name_div_xpath = "//*[contains(@text, '{}')]"
        try:
            self.driver.find_element_by_xpath(ent_first_name_div_xpath.format('统一社会信用代码')).click()
        except Exception as e:
            print(e)
            return

        # 2. 首先判断是否为社会组织
        if self.is_regime(ent_name):
            self.back_to_search()
            return

        ent_info_overview_title = ["企业年报", "行政处罚", "环保处罚", "经营异常", "税务异常", "严重违法失信", "司法拍卖", "清算信息", "破产重整", "新闻舆情"]
        for item in ent_info_overview_title:
            info, num = self.swipe_and_search_overview(item, ent_info_list)
            if int(num) > 0 and item == '行政处罚':
                ''' 进入列表页 '''
                info.click()
                try:
                    ''' 执行行政处罚爬取方法'''
                    if self.punish_detail(social_credit_code, 'administration'):
                        break
                    else:
                        self.default_move()
                except Exception as e:
                    # 无法定位到元素时，滑动一下继续搜索
                    print(e)
                    self.default_move()
                self.back_to_search()
            elif int(num) > 0 and item == '环保处罚':
                ''' 进入列表页 '''
                info.click()
                try:
                    ''' 执行环保处罚爬取方法'''
                    if self.env_detail(social_credit_code, 'env'):
                        break
                    else:
                        self.default_move()
                except Exception as e:
                    # 无法定位到元素时，滑动一下继续搜索
                    print(e)
                    self.default_move()
                self.back_to_search()

        # 4. 该企业的概览信息与详情信息处理完毕
        qxb_sql = '''
         INSERT INTO qxb (ent_name, social_credit_code,ent_year_report, administration_punish,environment_punish, manage_exception, tax_exception, outrage, judicial_sale, clearing_info, re_habilitation, news) 
         VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
        '''
        yj_nine_small_place_del_sql = '''
          DELETE FROM yj_nine_small_place WHERE unit_name = %s;
        '''

        self.loop.run_until_complete(self.save_info(qxb_sql, ent_info_list))
        self.loop.run_until_complete(self.save_info(yj_nine_small_place_del_sql, ent_info_list[0]))

        # 5. 回到原输入框
        self.back_to_search()

    ''' 
    2. 获取行政处罚详情页数据
    1）若行政处罚有数据时点击进入列表页
    2）获取当前列表页的概览信息按钮，遍历点击，进入每一条详情页面
    3）获取详情页数据后回到列表页，执行一次下拉，判断是否为最后一页，不是的话重复 2 的操作
    4）如果为列表页最后一页的话，退出
    '''
    def punish_detail(self, code, punish_type):
        # 根据处罚日期获取点击按钮
        ent_first_name_div_xpath = "//*[contains(@text, '{}')]"
        time.sleep(1)  # 频繁操作时解决找不到元素的异常
        # 处罚日期为第一个可定位元素，所以用这个定位
        # target_titles = ['决定书文号', '处罚类型', '处罚机关', '处罚日期', '公示日期', '处罚内容', '处罚依据']
        target_titles = ['tv_number', 'tv_content', 'tv_department', 'tv_date1', 'tv_date2', 'tv_detail', 'tv_punishment_basis']
        format_key = '处罚日期'
        detail_btns = self.driver.find_elements_by_xpath(ent_first_name_div_xpath.format(format_key))
        punish_list = []
        '''
        列表页按钮集合
        '''
        for btn in detail_btns:
            # 3. 点击进入详情页
            btn.click()
            punish = [code]
            # 4. 获取每条处罚详情页的每一项
            for title in target_titles:
                punish.append(self.driver.find_element_by_id(title).get_attribute('text'))
            ''' 修改为查询一次保存一条 '''
            punish.append(punish_type)
            sql = '''
            INSERT INTO punish_test (social_credit_code, number, content, department, date1, date2, detail, punishment_basis,push_type) 
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);
            '''
            print('punish{}', punish)
            self.loop.run_until_complete(self.save_info(sql, punish))
            # print('详情页{}', punish_list)
            # 5. 退出详情页
            self.back_to_search()

        '''
        当前列表页数据处理完毕后
        尝试执行一次下拉
        '''
        if self.is_final_page():
            ''' 确保该段代码只能通过此出口退出 '''
            print(' ===  详情页处理完毕  ===')
            return
        else:
            self.punish_detail(code, punish_type)

    """ 3. 获取环保处罚详情页"""
    def env_detail(self, code, punish_type):
        # 根据处罚日期获取点击按钮
        ent_first_name_div_xpath = "//*[contains(@text, '{}')]"
        time.sleep(1)  # 频繁操作时解决找不到元素的异常
        # target_titles = ['决定书文号', '处罚类型', '处罚机关', '处罚日期', '公示日期', '处罚内容', '处罚依据']
        target_titles = ['tv_number', 'tv_type', 'tv_department', 'tv_date1', 'tv_date2', 'tv_detail', 'tv_punishment_basis']
        format_key = '处罚类型'
        detail_btns = self.driver.find_elements_by_xpath(ent_first_name_div_xpath.format(format_key))
        punish_list = []
        for btn in detail_btns:
            # 3. 点击进入详情页
            btn.click()
            # punish = {'code': code}
            punish = [code]
            # 4. 获取每条处罚详情页的每一项
            for title in target_titles:
                try:
                    punish.append(self.driver.find_element_by_id(title).get_attribute('text'))
                except Exception as e:
                    print(e, '获取详情页异常')
            punish.append(punish_type)
            sql = '''
            INSERT INTO punish_test (social_credit_code, number, content, department, date1, date2, detail, punishment_basis,push_type) 
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);
            '''
            # print('punish', punish)
            # print('punish.values()', punish.values())
            self.loop.run_until_complete(self.save_info(sql, punish))
            punish_list.append(punish)
            print('详情页{}', punish)
            # 5. 退出详情页
            self.back_to_search()

        # 尝试执行一次下拉 确保该段代码只能通过此出口退出
        if self.is_final_page():
            print(' ===  获取环保处罚详情页处理完毕  ===')
            return True
        else:
            self.env_detail(code, punish_type)

    """ 滑动并搜索企业概览页面相关信息 """
    def swipe_and_search_overview(self, title, ent_info):
        while True:
            try:
                self.driver.find_element_by_xpath("//android.widget.TextView[@text='{}']".format(title))  # 寻找元素
                base_element = "//android.widget.TextView[@text='{}']/preceding-sibling::android.widget.TextView[1]"
                info = self.driver.find_element_by_xpath(base_element.format(title))
                num = info.get_attribute("text")
                ent_info.append(num)
                # 处理行政处罚详情页
                return info, num
            except Exception as e:
                print('概览页查询异常', e)
                self.default_move()

    """ 首先判断是否为社会组织 """
    def is_regime(self, ent_name):
        regime_id = "tv_title_name"
        try:
            self.driver.find_element_by_id(regime_id)
            regime_list = [ent_name, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
            self.loop.run_until_complete(self.save_info(regime_list))
            return True
        except Exception as e:
            print(e)
            return False

    """ 爬取异常时发送邮件 """
    def send_email(self):
        from_addr = self.cfg['from_addr']
        password = self.cfg['password']
        to_addr = self.cfg['to_addr']
        smtp_server = self.cfg['smtp_server']
        # 邮箱正文内容，第一个参数为内容，第二个参数为格式(plain 为纯文本)，第三个参数为编码
        msg = MIMEText('send by python', 'plain', 'utf-8')
        # 邮件头信息
        msg['From'] = Header(from_addr)
        msg['To'] = Header(to_addr)
        msg['Subject'] = Header(self.cfg['Subject'])
        # 开启发信服务，这里使用的是加密传输
        server = smtplib.SMTP_SSL(smtp_server)
        server.connect(smtp_server, self.cfg['port'])
        # 登录发信邮箱
        server.login(from_addr, password)
        # 发送邮件
        server.sendmail(from_addr, to_addr, msg.as_string())
        # 关闭服务器
        server.quit()

    """ 判断是否滑动到底部 """
    def is_final_page(self):
        print('是否滑动到底部')
        # 获取滑动前页面元素
        before_swipe = self.driver.page_source
        # 滑动
        self.big_move()
        # 获取滑动后页面元素
        after_swipe = self.driver.page_source
        # 到达尾页
        if before_swipe == after_swipe:
            return True
        # 若滑动前后页面元素不同，则滑动成功，继续滑动
        else:
            return False

    """ 持久化方法 """
    async def save_info(self, sql, data):
        # 插入重复数据的异常，暂时不考虑其他
        try:
            self.cursor.execute(sql, data)
            self.conn.commit()
            print( "sql 信息已处理完毕")
        except Exception as e:
            print('sql 插入重复数据的异常{}', e)

    """ 默认滑动 """
    def default_move(self):
        self.driver.swipe(self.width, self.height * 0.7, self.width, self.height * 0.5)

    """ 默认滑动 """
    def big_move(self):
        self.driver.swipe(self.width, self.height * 0.7, self.width, self.height * 0.1)  # 滑动屏幕


# test
qxb = qxb_spider()
qxb.first_in()
# ent_list = qxb.ent_list_task()
# try:
#     for ent in ent_list:
#         time.sleep(1)  # 不同手机配置不一样，可能不及时刷新，等一秒
#         print(ent[0], ent[1], '开始')
#         qxb.get_info(ent[0], ent[1])
# except Exception as e:
#     qxb.send_email()
# print('本次任务结束')



qxb.get_info('91320100608982022N', '南京百江液化气有限公司')
# qxb.get_info('91320100608982022N', '南京百江液化气有限公司')
